home *** CD-ROM | disk | FTP | other *** search
- ;=========================================================================
- ; XSCALE1.ASM by John A. Slagel, jas37876@uxa.cso.uiuc.edu
- ; This is some code to do bitmap scaling in VGA Mode X. It can scale a
- ; bitmap of any size down to 2 pixels wide, or up to thousands of pixels
- ; wide. It performs complete clipping, with only a small constant amount
- ; of time to clip, no matter how huge the image is. It draws column by
- ; column to reduce the number of plane switches, which are slow. The inner
- ; column loop has been optimized for no memory accesses, except to read or
- ; write a pixel. This uses MASM 5.1 features, and can be compiled in any
- ; memory model by changing the .MODEL line, but make sure that you always
- ; pass a far pointer to the bitmap data, regardless of memory model.
- ; C-callable as:
- ; void xscale( int X, int Y, int DW, int DY,
- ; int SW, int SH, int offs, void far * Bitmap );
- ;
- ; void xscale_masked( int X, int Y, int DW, int DY,
- ; int SW, int SH, int offs, void far * Bitmap );
- ;
- ; X,Y are the upper left-hand coordinates of where to draw the bitmap.
- ; DW,DH are the width and height of the SCALEed bitmap
- ; SW,SH are the width and height of the source bitmap.
- ; Bitmap is a pointer to the bitmap bits.
- ;
- ; minor mods by AJR Aug 1994 to make it work with XLIB's addressing.
- ; xscale_masked() added by AJR Sept 1994, only had to add two lines
- ; to do masked scaling. Assembled just fine with TASM 3.1
- ; Alexander J. Russell Internet: alexad3@icebox.iceonline.com
- ;
- ; NOTE:
- ; The bitnap pointer points to a plain linear bitmap as used for mode13.
- ;
- ;==========================================================================
- include xlib.inc
- include xscale.inc
-
- SC_INDEX2 EQU 03C5h ; Port number of VGA Seqeuncer Data
-
- .DATA
-
-
- ClipLt DW 0 ; Left clipping boundry
- ClipRt DW 359 ; Right clipping boundry
- ClipTp DW 0 ; Top clipping boundry
- ClipBt DW 239 ; Bottom clipping boundry
-
- .CODE
-
- xscale PROC DestX:WORD, DestY:WORD, \
- DestWidth:WORD, DestHeight:WORD, \
- SourceWidth:WORD, SourceHeight:WORD, \
- ScrnOffs:WORD, \
- Bitmap:FAR PTR
- LOCAL DecisionX:WORD, DecisionY:WORD, ClippedWidth:WORD, ClippedHeight:WORD
-
- push si
- push di
- push ds
-
- cmp DestWidth, 2 ; If destination width is less than 2
- jl Done ; then don't draw it.
-
- cmp DestHeight, 2 ; If destination height is less than 2
- jl Done ; then don't draw it.
-
- mov ax, DestY ; If it is completely below the
- cmp ax, ClipBt ; lower clip bondry,
- jg Done ; then don't draw it.
-
- add ax, DestHeight ; If it is above clip boundries
- dec ax ; then don't draw it.
- cmp ax, ClipTp
- jl Done
-
- mov ax, DestX ; If it is to the right of the
- mov cx, ClipRt ; right clip boundry
- cmp ax, ClipRt ; then don't draw it.
- jg Done
-
- add ax, DestWidth ; If it is completely to the left
- dec ax ; of the left clip boundry,
- cmp ax, ClipLt ; then don't draw it.
- jl Done
-
- les si, Bitmap ; Make DS:SI point to bitmap data
-
- mov ax, DestWidth ; ClippedWidth is initially set to
- mov ClippedWidth, ax ; the requested dest width.
-
- shl ax,1 ; Initialize the X decision var
- neg ax ; to be -2*DestWidth
- mov DecisionX, ax ;
-
- mov ax, DestHeight ; ClippedHeight is initially set to
- mov ClippedHeight, ax ; the requested dest size.
-
- shl ax,1 ; Initialize the Y decision var
- neg ax ; to be -2*DestHeight
- mov DecisionY, ax ;
-
- movsx eax, ClipTp ; If Y is below the top
- mov edx, eax ; clipping boundry, then we don't
- sub dx, DestY ; need to clip the top, so we can
- js NoTopClip ; jump over the clipping stuff.
-
- mov DestY, ax ; This block performs clipping on the
- sub ClippedHeight, dx ; top of the bitmap. I have heavily
- movsx ecx, SourceHeight ; optimized this block to use only 4
- imul ecx, edx ; 32-bit registers, so I'm not even
- mov eax, ecx ; gonna try to explain what it's doing.
- mov edx, 0 ; But I can tell you what results from
- movsx ebx, DestHeight ; this: The DecisionY var is updated
- idiv ebx ; to start at the right clipped row.
- movsx edx, SourceWidth ; Y is moved to the top clip
- imul edx, eax ; boundry. ClippedHeight is lowered since
- add si, dx ; we won't be drawing all the requested
- imul eax, ebx ; rows. SI is changed to point over
- sub ecx, eax ; the bitmap data that is clipped off.
- sub ecx, ebx ;
- shl ecx, 1 ;
- mov DecisionY, cx ; <end of top clipping block >
-
- NoTopClip:
- mov ax, DestY ; If the bitmap doesn't extend over the
- add ax, ClippedHeight ; bottom clipping boundry, then we
- dec ax ; don't need to clip the bottom, so we
- cmp ax, ClipBt ; can jump over the bottom clip code.
- jle NoBottomClip ;
-
- mov ax, ClipBt ; Clip off the bottom by reducing the
- sub ax, DestY ; ClippedHeight so that the bitmap won't
- inc ax ; extend over the lower clipping
- mov ClippedHeight, ax ; boundry.
-
- NoBottomClip:
- movsx eax, ClipLt ; If X is to the left of the
- mov edx, eax ; top clipping boundry, then we don't
- sub dx, DestX ; need to clip the left, so we can
- js NoLeftClip ; jump over the clipping stuff.
-
- mov DestX, ax ; This block performs clipping on the
- sub ClippedWidth, dx ; left of the bitmap. I have heavily
- movsx ecx, SourceWidth ; optimized this block to use only 4
- imul ecx, edx ; 32-bit registers, so I'm not even
- mov eax, ecx ; gonna try to explain what it's doing.
- mov edx, 0 ; But I can tell you what results from
- movsx ebx, DestWidth ; this: The DecisionX var is updated
- idiv ebx ; to start at the right clipped column.
- add si, ax ; X is moved to the left clip
- imul eax, ebx ; boundry. ClippedWidth is reduced since
- sub ecx, eax ; we won't be drawing all the requested
- sub ecx, ebx ; cols. SI is changed to point over
- shl ecx, 1 ; the bitmap data that is clipped off.
- mov DecisionX, cx ; <end of left clipping block >
-
- NoLeftClip:
- mov ax, DestX ; If the bitmap doesn't extend over the
- add ax, ClippedWidth ; right clipping boundry, then we
- dec ax ; don't need to clip the right, so we
- cmp ax, ClipRt ; can jump over the right clip code.
- jle NoClipRight ;
-
- mov ax, ClipRt ; Clip off the right by reducing the
- sub ax, DestX ; ClippedWidth so that the bitmap won't
- inc ax ; extend over the right clipping
- mov ClippedWidth, ax ; boundry.
-
- NoClipRight:
-
- ; only this little bit here modded by AJR for XLIB addresing
- ; Calculate starting video address in
- mov ax, DestY ; VGA memory. This code sets DS to the
- mov bx, ScrnLogicalByteWidth ; (global var, needs DS intact)
- mul bx ; VGA segment, which is usually at
- mov di, ScrnOffs ; 0a000
- add di, ax
- mov ax, SCREEN_SEG ; We are going to set DS:DI to point
- mov ds, ax ; to the start point in vid mem to start
- mov ax, DestX ; drawing at.
- mov cx, ax ; The offset DI is calculated by:
- shr ax, 2 ; DI = Y*WIDTH+X/4 + offset
- add di, ax ; DS:DI is ready!
- ; end of mods
-
- mov dx, SC_INDEX ; Point the VGA Sequencer to the Map
- mov al, MAP_MASK ; Mask register, so that we only need
- out dx, al ; to send out 1 byte per column.
-
- inc dx ; Move to the Sequencer's Data register.
- and cx, 3 ; Calculate the starting plane. This is
- mov al, 11h ; just:
- shl al, cl ; Plane = (11h << (X AND 3))
- out dx, al ; Select the first plane.
-
- ALIGN 2 ; Since this point gets jumped to a lot,
- ; make sure that it is DWORD aligned.
- RowLoop:
- push si ; Save the starting source index
- push di ; Save the starting dest index
- push ax ; Save the current plane mask
- push bp ; Save the current base pointer
-
- mov ax, ClippedHeight ; Use AL for row counter (0-239)
- mov bx, DecisionY ; Use BX for decision variable
- mov cx, SourceWidth ; Use CX for source width
- mov dx, SourceHeight ; Use DX for source height * 2
- shl dx, 1
- mov bp, DestHeight ; Use BP for dest height * 2
- shl bp, 1
- mov ah, es:[si] ; Get the first source pixel
-
- ALIGN 2 ; Common jump point... align for speed.
- ColumnLoop:
- mov ds:[di], ah ; Draw a pixel
- dec al ; Decrement line counter
- jz DoneWithCol ; See if we're done with this column
- add di, 90 ; Go on to the next screen row
- add bx, dx ; Increment the decision variable
- js ColumnLoop ; Draw this source pixel again
-
- IncSourceRow:
- add si, cx ; Move to the next source pixel
- sub bx, bp ; Decrement the decision variable
- jns IncSourceRow ; See if we need to skip another source pixel
- mov ah, es:[si] ; Get the next source pixel
- jmp ColumnLoop ; Start drawing this pixel
-
- DoneWithCol:
- pop bp ; Restore BP to access variables
- pop ax ; Restore AL = plane mask
- pop di ; Restore DI to top row of screen
- pop si ; Restore SI to top row of source bits
-
- rol al, 1 ; Move to next plane
- adc di, 0 ; Go on to next screen column
- mov dx, SC_INDEX2 ; Tell the VGA what column we're in
- out dx, al ; by updating the map mask register
-
- shl cx, 1 ; CX = SourceWidth * 2
- mov bx, DecisionX ; Use BX for the X decision variable
- add bx, cx ; Increment the X decision variable
- js NextCol ; Jump if we're still in the same source col.
- mov dx, DestWidth ; DX = W * 2
- shl dx, 1
- IncSourceCol:
- inc si ; Move to next source column
- sub bx, dx ; Decrement X decision variable
- jns IncSourceCol ; See if we skip another source column
- NextCol:
- mov DecisionX, bx ; Free up BX for ColLoop
- dec ClippedWidth ; If we're not at last column
- jnz RowLoop ; then do another column
- Done:
- pop ds ; restore data segment
- pop di ; restore registers
- pop si
-
-
- ret ; We're done!
-
- xscale ENDP
-
-
-
-
- xscale_masked PROC DestX:WORD, DestY:WORD, \
- DestWidth:WORD, DestHeight:WORD, \
- SourceWidth:WORD, SourceHeight:WORD, \
- ScrnOffs:WORD, \
- Bitmap:FAR PTR
- LOCAL DecisionX:WORD, DecisionY:WORD, ClippedWidth:WORD, ClippedHeight:WORD
-
- push si
- push di
- push ds
-
- cmp DestWidth, 2 ; If destination width is less than 2
- jl m_Done ; then don't draw it.
-
- cmp DestHeight, 2 ; If destination height is less than 2
- jl m_Done ; then don't draw it.
-
- mov ax, DestY ; If it is completely below the
- cmp ax, ClipBt ; lower clip bondry,
- jg m_Done ; then don't draw it.
-
- add ax, DestHeight ; If it is above clip boundries
- dec ax ; then don't draw it.
- cmp ax, ClipTp
- jl m_Done
-
- mov ax, DestX ; If it is to the right of the
- mov cx, ClipRt ; right clip boundry
- cmp ax, ClipRt ; then don't draw it.
- jg m_Done
-
- add ax, DestWidth ; If it is completely to the left
- dec ax ; of the left clip boundry,
- cmp ax, ClipLt ; then don't draw it.
- jl m_Done
-
- les si, Bitmap ; Make DS:SI point to bitmap data
-
- mov ax, DestWidth ; ClippedWidth is initially set to
- mov ClippedWidth, ax ; the requested dest width.
-
- shl ax,1 ; Initialize the X decision var
- neg ax ; to be -2*DestWidth
- mov DecisionX, ax ;
-
- mov ax, DestHeight ; ClippedHeight is initially set to
- mov ClippedHeight, ax ; the requested dest size.
-
- shl ax,1 ; Initialize the Y decision var
- neg ax ; to be -2*DestHeight
- mov DecisionY, ax ;
-
- movsx eax, ClipTp ; If Y is below the top
- mov edx, eax ; clipping boundry, then we don't
- sub dx, DestY ; need to clip the top, so we can
- js m_NoTopClip ; jump over the clipping stuff.
-
- mov DestY, ax ; This block performs clipping on the
- sub ClippedHeight, dx ; top of the bitmap. I have heavily
- movsx ecx, SourceHeight ; optimized this block to use only 4
- imul ecx, edx ; 32-bit registers, so I'm not even
- mov eax, ecx ; gonna try to explain what it's doing.
- mov edx, 0 ; But I can tell you what results from
- movsx ebx, DestHeight ; this: The DecisionY var is updated
- idiv ebx ; to start at the right clipped row.
- movsx edx, SourceWidth ; Y is moved to the top clip
- imul edx, eax ; boundry. ClippedHeight is lowered since
- add si, dx ; we won't be drawing all the requested
- imul eax, ebx ; rows. SI is changed to point over
- sub ecx, eax ; the bitmap data that is clipped off.
- sub ecx, ebx ;
- shl ecx, 1 ;
- mov DecisionY, cx ; <end of top clipping block >
-
- m_NoTopClip:
- mov ax, DestY ; If the bitmap doesn't extend over the
- add ax, ClippedHeight ; bottom clipping boundry, then we
- dec ax ; don't need to clip the bottom, so we
- cmp ax, ClipBt ; can jump over the bottom clip code.
- jle m_NoBottomClip ;
-
- mov ax, ClipBt ; Clip off the bottom by reducing the
- sub ax, DestY ; ClippedHeight so that the bitmap won't
- inc ax ; extend over the lower clipping
- mov ClippedHeight, ax ; boundry.
-
- m_NoBottomClip:
- movsx eax, ClipLt ; If X is to the left of the
- mov edx, eax ; top clipping boundry, then we don't
- sub dx, DestX ; need to clip the left, so we can
- js m_NoLeftClip ; jump over the clipping stuff.
-
- mov DestX, ax ; This block performs clipping on the
- sub ClippedWidth, dx ; left of the bitmap. I have heavily
- movsx ecx, SourceWidth ; optimized this block to use only 4
- imul ecx, edx ; 32-bit registers, so I'm not even
- mov eax, ecx ; gonna try to explain what it's doing.
- mov edx, 0 ; But I can tell you what results from
- movsx ebx, DestWidth ; this: The DecisionX var is updated
- idiv ebx ; to start at the right clipped column.
- add si, ax ; X is moved to the left clip
- imul eax, ebx ; boundry. ClippedWidth is reduced since
- sub ecx, eax ; we won't be drawing all the requested
- sub ecx, ebx ; cols. SI is changed to point over
- shl ecx, 1 ; the bitmap data that is clipped off.
- mov DecisionX, cx ; <end of left clipping block >
-
- m_NoLeftClip:
- mov ax, DestX ; If the bitmap doesn't extend over the
- add ax, ClippedWidth ; right clipping boundry, then we
- dec ax ; don't need to clip the right, so we
- cmp ax, ClipRt ; can jump over the right clip code.
- jle m_NoClipRight ;
-
- mov ax, ClipRt ; Clip off the right by reducing the
- sub ax, DestX ; ClippedWidth so that the bitmap won't
- inc ax ; extend over the right clipping
- mov ClippedWidth, ax ; boundry.
-
- m_NoClipRight:
-
- ; only this little bit here modded by AJR for XLIB addressing
- ; Calculate starting video address in
- mov ax, DestY ; VGA memory. This code sets DS to the
- mov bx, ScrnLogicalByteWidth ; (global var, needs DS intact)
- mul bx ; VGA segment, which is usually at
- mov di, ScrnOffs ; 0a000
- add di, ax
- mov ax, SCREEN_SEG ; We are going to set DS:DI to point
- mov ds, ax ; to the start point in vid mem to start
- mov ax, DestX ; drawing at.
- mov cx, ax ; The offset DI is calculated by:
- shr ax, 2 ; DI = Y*WIDTH+X/4 + offset
- add di, ax ; DS:DI is ready!
- ; end of mods
-
- mov dx, SC_INDEX ; Point the VGA Sequencer to the Map
- mov al, MAP_MASK ; Mask register, so that we only need
- out dx, al ; to send out 1 byte per column.
-
- inc dx ; Move to the Sequencer's Data register.
- and cx, 3 ; Calculate the starting plane. This is
- mov al, 11h ; just:
- shl al, cl ; Plane = (11h << (X AND 3))
- out dx, al ; Select the first plane.
-
- ALIGN 2 ; Since this point gets jumped to a lot,
- ; make sure that it is DWORD aligned.
- m_RowLoop:
- push si ; Save the starting source index
- push di ; Save the starting dest index
- push ax ; Save the current plane mask
- push bp ; Save the current base pointer
-
- mov ax, ClippedHeight ; Use AL for row counter (0-239)
- mov bx, DecisionY ; Use BX for decision variable
- mov cx, SourceWidth ; Use CX for source width
- mov dx, SourceHeight ; Use DX for source height * 2
- shl dx, 1
- mov bp, DestHeight ; Use BP for dest height * 2
- shl bp, 1
- mov ah, es:[si] ; Get the first source pixel
-
- ALIGN 2 ; Common jump point... align for speed.
- m_ColumnLoop:
- or ah,ah ; mask off 0 pixels a la Xlib
- jz m_no_draw
- mov ds:[di], ah ; Draw a pixel
- m_no_draw:
- dec al ; Decrement line counter
- jz m_DoneWithCol ; See if we're done with this column
- add di, 90 ; Go on to the next screen row
- add bx, dx ; Increment the decision variable
- js m_ColumnLoop ; Draw this source pixel again
-
- m_IncSourceRow:
- add si, cx ; Move to the next source pixel
- sub bx, bp ; Decrement the decision variable
- jns m_IncSourceRow ; See if we need to skip another source pixel
- mov ah, es:[si] ; Get the next source pixel
- jmp m_ColumnLoop ; Start drawing this pixel
-
- m_DoneWithCol:
- pop bp ; Restore BP to access variables
- pop ax ; Restore AL = plane mask
- pop di ; Restore DI to top row of screen
- pop si ; Restore SI to top row of source bits
-
- rol al, 1 ; Move to next plane
- adc di, 0 ; Go on to next screen column
- mov dx, SC_INDEX2 ; Tell the VGA what column we're in
- out dx, al ; by updating the map mask register
-
- shl cx, 1 ; CX = SourceWidth * 2
- mov bx, DecisionX ; Use BX for the X decision variable
- add bx, cx ; Increment the X decision variable
- js m_NextCol ; Jump if we're still in the same source col.
- mov dx, DestWidth ; DX = W * 2
- shl dx, 1
- m_IncSourceCol:
- inc si ; Move to next source column
- sub bx, dx ; Decrement X decision variable
- jns m_IncSourceCol ; See if we skip another source column
- m_NextCol:
- mov DecisionX, bx ; Free up BX for ColLoop
- dec ClippedWidth ; If we're not at last column
- jnz m_RowLoop ; then do another column
- m_Done:
- pop ds ; restore data segment
- pop di ; restore registers
- pop si
-
-
- ret ; We're done!
-
- xscale_masked ENDP
-
-
- END
-